/*
 * Copyright (c) 2019.11，华乘电气科技有限公司
 * All rights reserved.
 *
 * 文件名称：jsonprocesshelper.h
 * 
 * 初始版本：1.0
 * 作者：洪澄
 * 创建日期：2019/11/07
 * 摘要：JSON处理助手
 * 当前版本：1.0
 */

#pragma once

#include <vector>
#include <map>
#include <QString>
#include "include/rapidjson/rapidjson.h"
#include "include/rapidjson/document.h"
#include "include/rapidjson/prettywriter.h"

namespace DataSpecificationGW
{
    typedef rapidjson::Document::AllocatorType allocator_t;

    template <bool pretty> struct json_writer_t;

    template <>
    struct json_writer_t <true>
    {
        typedef rapidjson::PrettyWriter<rapidjson::StringBuffer> writer_t;
    };

    template <>
    struct json_writer_t <false>
    {
        typedef rapidjson::Writer<rapidjson::StringBuffer> writer_t;
    };

    template<bool pretty>
    bool saveJSON(const rapidjson::Document& doc, std::string& jsonValue)
    {
        rapidjson::StringBuffer buffer;
        typename json_writer_t<pretty>::writer_t writer(buffer);
        doc.Accept(writer);
        jsonValue = buffer.GetString();
        return true;
    }

    bool saveJSONDocument(rapidjson::Value& dst, rapidjson::Document& src);
    bool parseJSONDocument(rapidjson::Document& src, rapidjson::Value& dst);

    template<bool pretty, typename T>
    bool saveJSON(const T& objValue, std::string& jsonValue)
    {
        rapidjson::Document doc;
        rapidjson::Value value;
        if (!saveJSON(objValue, doc.GetAllocator(), value))
        {
            return false;
        }
        if (!saveJSONDocument(value, doc))
        {
            return false;
        }
        return saveJSON <pretty>(doc, jsonValue);
    }

    template<typename T>
    bool parseJSON(const char * jsonValue, T& objValue)
    {
        rapidjson::Document doc;
        doc.Parse(jsonValue);
        rapidjson::Value value;
        if (!parseJSONDocument(doc, value))
        {
            return false;
        }
        return parseJSON(value, objValue);
    }
    template<typename T>
    bool parseJSON(const std::string& jsonValue, T& objValue)
    {
        return parseJSON(jsonValue.c_str(), objValue);
    }

    bool saveJSON(const std::string& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, std::string& objValue);

    //////////////////////////////////////////////////////////////////////////
    //
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
    template<typename T,
             typename Allocator,
             typename JsonValue,
             typename DecayT = typename std::decay<T>::type,
             bool isUnsigned = std::is_unsigned<DecayT>::value,
             bool isBool = std::is_same<DecayT, bool>::value,
             bool isInt64 = std::is_same<DecayT, int64_t>::value,
             bool isUInt64 = std::is_same<DecayT, uint64_t>::value>
    typename std::enable_if<std::is_integral<DecayT>::value, bool>::type saveJSON(T&& objValue, Allocator&& alloc, JsonValue&& jsonValue)
    {
        if (isBool)
        {
            jsonValue.SetBool(objValue);
            return true;
        }
        else
        {
            if (isUnsigned)
            {
                if(isUInt64)
                {
                    jsonValue.SetUint64(objValue);
                    return true;
                }
                else
                {
                    jsonValue.SetUint(objValue);
                    return true;
                }
            }
            else
            {
                if (isInt64)
                {
                    jsonValue.SetInt64(objValue);
                    return true;
                }
                else
                {
                    jsonValue.SetInt(objValue);
                    return true;
                }
            }
        }

        return false;
    }

    template<typename T>
    typename std::enable_if<std::is_floating_point<typename std::decay<T>::type>::value, bool>::type saveJSON(T&& objValue, allocator_t& alloc, rapidjson::Value& jsonValue)
    {
        jsonValue.SetDouble(objValue);
        return true;
    }

    template<typename T,
             bool isBool = std::is_same<T, bool>::value>
    typename std::enable_if<std::is_integral<typename std::decay<T>::type>::value, bool>::type parseJSON(const rapidjson::Value& jsonValue, T&& objValue)
    {
        if (isBool)
        {
            if (jsonValue.IsBool())
            {
                objValue = jsonValue.GetBool();
                return true;
            }
            else if (jsonValue.IsInt())
            {
                int tmp = jsonValue.GetInt();
                if (!tmp)
                {
                    objValue = false;
                }
                else
                {
                    objValue = true;
                }
                return true;
            }
        }
        else
        {
            if (jsonValue.IsInt64())
            {
                objValue = jsonValue.GetInt64();
                return true;
            }
            else if (jsonValue.IsUint64())
            {
                objValue = jsonValue.GetUint64();
                return true;
            }
            else if (jsonValue.IsInt())
            {
                objValue = jsonValue.GetInt();
                return true;
            }
            else if (jsonValue.IsUint())
            {
                objValue = jsonValue.GetUint();
                return true;
            }
            else if (jsonValue.IsBool())
            {
                objValue = jsonValue.GetBool();
                return true;
            }
        }

        return false;
    }

    template<typename T>
    typename std::enable_if<std::is_floating_point<typename std::decay<T>::type>::value, bool>::type parseJSON(const rapidjson::Value& jsonValue, T&& objValue)
    {
        if (jsonValue.IsNumber())
        {
            objValue = jsonValue.GetDouble();
            return true;
        }

        return false;
    }
#else
    bool saveJSON(const int32_t& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, int32_t& objValue);

    bool saveJSON(const uint32_t& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, uint32_t& objValue);

    bool saveJSON(const double& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, double & objValue);

    bool saveJSON(const int64_t& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, int64_t& objValue);

    bool saveJSON(const uint64_t& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, uint64_t& objValue);

    bool saveJSON(const int8_t& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, int8_t& objValue);

    bool saveJSON(const uint8_t& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, uint8_t& objValue);

    bool saveJSON(const int16_t& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, int16_t& objValue);

    bool saveJSON(const uint16_t& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, uint16_t& objValue);

    bool saveJSON(const float& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, float & objValue);
#endif

    template<typename T>
    bool saveJSON(const std::vector<T>& objValue, allocator_t& alloc, rapidjson::Value& jsonValue)
    {
        jsonValue.SetArray();
        typename std::vector<T>::const_iterator it;
        for (it = objValue.begin(); it != objValue.end(); ++it)
        {
            rapidjson::Value tmpValue;
            if (!saveJSON(*it, alloc, tmpValue))
            {
                return false;
            }
            jsonValue.PushBack(tmpValue.Move(), alloc);
        }
        return true;
    }

    template<typename T>
    bool parseJSON(const rapidjson::Value& jsonValue, std::vector<T>& objValue)
    {
        if (!jsonValue.IsArray())
        {
            return false;
        }
        rapidjson::SizeType size = jsonValue.Size();
        objValue.reserve(size);

        for (rapidjson::SizeType i = 0; i < size; ++i)
        {
            T tmpValue;
            if (!parseJSON(jsonValue[i], tmpValue))
            {
                return false;
            }
            objValue.push_back(tmpValue);
        }
        return true;
    }

    template<typename T>
    bool saveJSON(const std::map<std::string, T>& objValue, allocator_t& alloc, rapidjson::Value& jsonValue)
    {
        jsonValue.SetObject();
        typename std::map<std::string, T>::const_iterator it;
        for (it = objValue.begin(); it != objValue.end(); ++it)
        {
            rapidjson::Value tmpValue;
            if (!saveJSON(it->second, alloc, tmpValue))
            {
                return false;
            }
            jsonValue.AddMember(rapidjson::StringRef(it->first.c_str()), tmpValue.Move(), alloc);
        }
        return true;
    }

    template<typename T>
    bool parseJSON(const rapidjson::Value& jsonValue, std::map<std::string, T>& objValue)
    {
        if (!jsonValue.IsObject())
        {
            return false;
        }
        for (rapidjson::Value::ConstMemberIterator it = jsonValue.MemberBegin(); it != jsonValue.MemberEnd(); ++it)
        {
            T tmpValue;
            if (!parseJSON(it->value, tmpValue))
            {
                return false;
            }
            objValue[it->name.GetString()] = tmpValue;
        }
        return true;
    }
    //    template<typename T>
    //    typename std::enable_if<std::is_same<typename std::decay<T>::type, int64_t>::value, bool>::type parseJSON(const rapidjson::Value& jsonValue, T&& objValue)
    //    {
    //        if (jsonValue.IsInt64())
    //        {
    //            objValue = jsonValue.GetInt64();
    //            return true;
    //        }
    //        else if (jsonValue.IsInt())
    //        {
    //            objValue = jsonValue.GetInt();
    //            return true;
    //        }
    //        else if (jsonValue.IsUint())
    //        {
    //            objValue = jsonValue.GetUint();
    //            return true;
    //        }
    //        return true;
    //    }



    //////////////////////////////////////////////////////////////////////////
    //

    bool saveJSON(const QString& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, QString& objValue);

    bool saveJSON(const QByteArray& objValue, allocator_t& alloc, rapidjson::Value& jsonValue);
    bool parseJSON(const rapidjson::Value& jsonValue, QByteArray& objValue);


    //////////////////////////////////////////////////////////////////////////
    //

    template<typename T>
    bool saveJSONField(const T& field, const char* pFiledName, allocator_t& alloc, rapidjson::Value& jsonValue)
    {
        if (!pFiledName)
        {
            return false;
        }
        rapidjson::Value jsonValueTmp;
        if (!saveJSON(field, alloc, jsonValueTmp))
        {
            return false;
        }
        rapidjson::Value key(pFiledName, alloc);
        jsonValue.AddMember(key, jsonValueTmp.Move(), alloc);
        return true;
    }

    template<typename T>
    bool parseJSONField(const rapidjson::Value& jsonValue, const char* pFiledName, T& field, bool& bFieldExist)
    {
        bFieldExist = false;
        if (!jsonValue.IsObject() || !pFiledName)
        {
            return false;
        }
        if (jsonValue.HasMember(pFiledName))
        {
            bFieldExist = true;
            const rapidjson::Value& jsonValueTmp = jsonValue[pFiledName];
            if (!parseJSON(jsonValueTmp, field))
            {
                return false;
            }
        }
        return true;
    }

}
